home *** CD-ROM | disk | FTP | other *** search
- WBStartup
- ;This programme was the subject of a brief demonstration at the North Shore
- ;meeting on 27/7/94. I was tempted to explore algorithmic composition after
- ;reading Douglas Hofstadter's excellent book "Godel, Esher, Bach" in which
- ;he remarks that in his opinion meaningful computer composition is many years away.
- ;For some reason I disagree with him although I would be hard put to construct
- ;a logical defence.
-
- ;Harmony, strangely enough, proved the least of problems. The real problem is
- ;to devise algorithms which produce cogent phrases, rhythms and forms.
- ;Mathematical form does not imply nice music; the serialists at least taught
- ;us that much. Aleatoric methods are just about as dull. Somewhere between
- ;perfect abstract form and the totally stochastic must lie areas of life and
- ;meaning. If computer programmes can help human brains to find them then
- ;so much the better.
-
- ;The whole field seems to me to be a grossly neglected area of research.
- ;It may be that the structures which give rise to the various emotive responses in
- ;music are quite simple. There is also the question of to what degree the
- ;listener imposes an emotional response on a piece of music. This propensity
- ;may be more powerful than we would like to believe. I remember playing the
- ;piano for a particular friend on several occasions and being mystified that
- ;his opinions contrasted sharply from one time to the next. I finally found
- ;that anything I played which included either ninth chords or syncopation was
- ;anathema and anything which did not include these things was deemed masterly!
- ;When I played a mixture of sections with and sections without he became
- ;irritable and wanted to stop for a drink.
-
- Filter Off
- Screen 0,10
- ScreensBitMap 0,0
- ShowScreen 0
- Use BitMap 0
- Cls 0
- BitMapOutput 0
- Locate 2,5:Print "A simple programme to compose fugues."
- Locate 2,7:Print "Hold right mouse button to stop."
-
- ;Periods
- Data.w 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906
- Data.w 856,808,762,720,678,640,604,570,538,508,480,453
- Data.w 428,404,381,360,339,320,302,285,269,254,240,226
- Data.w 214,202,190,180,170,160,151,143,135,127
-
- ;Data for "legitimate" harmonies for desired style.
- ;In this case very old-fashioned.
- ;Majors,minors,diminisheds,sevenths,sixths,minor sixths
- ;unisons,double notes etc. (whatever is desired)
- Data.w 652,422,522,642,542,412,4392,3062,5602,4502,4402
- Data.w 4272,5712,4512,3072,4282,4382,5722,3182
- Data.w 12,42,102,92,52,72
-
- ;Data for permissible modulations.
- Data.w 0,4,2,5,4,3,2,1,3,0,0,0,5,3,0,4,5,0,7,3,0,1,5,0,102,3,0,2,5,0,109,4,0,2,3,4
- Data.w 100,3,2,1,4,0,3,4,3,0,4,5,7,1,0,0,0,0,8,2,1,4,0,0,105,3,0,1,3,0,107,1,1,0,0,0
-
- Data.s theegg,cordpiano,piano,bubble,warmbells
-
- ;Sound structures
- NEWTYPE.chrom
- adr_sound.l
- ss.w
- period.w
- End NEWTYPE
-
- NEWTYPE.sound
- _data.l
- _period.w
- End NEWTYPE
-
- ;Set fugue length (number of shortest note lengths)
- fl.w=512:fl1.w=fl-1
-
- ;Dimension arrays
- Dim motifs.w(100)
- Dim voices.w(fl,4)
- Dim inst.w(fl)
- Dim chrom_scale.chrom(84)
- Dim chrom_scale2.chrom(84)
- Dim maj_scale.w(12,84)
- Dim min_scale.w(12,84)
- Dim maj_tonics.w(12)
- Dim min_tonics.w(12)
- Dim fugue.w(fl,4)
- Dim fixed.w(fl,4)
- Dim fugue_ctype.w(fl)
- Dim modes.w(fl)
- Dim tempi.w(fl)
- Dim t.w(3),interval.w(4),s.w(4),interval2.w(4),legit_chord.w(31)
- Dim fix_pos.w(4)
- Dim maj_shifts.w(6,6),min_shifts.w(6,6)
- Dim lastmstart.w(4)
- Dim insts.s(5)
-
-
- s.w=0
-
- ;Set length of fixed section motifs (opening)
- fix_length.w=32
- For i=0 To 3:fix_pos(i)=i*fix_length:Next i
-
- ;Set out chromatic scale
- For i=0 To 45
- Read chrom_scale(i)\period
- chrom_scale2(i)\period=chrom_scale(i)\period
- chrom_scale(i)\adr_sound=Addr Sound(0)
- chrom_scale(i)\ss=0
- chrom_scale2(i)\adr_sound=Addr Sound(2)
- chrom_scale2(i)\ss=2
- Next
-
- ;Read allowable chord types
- For i=0 To 24:Read legit_chord(i):Next i
-
- ;Read the tune
- ;For i=0 To 39:Read motifs(i):Next i
-
- ;Read permissible modulations
- For i=0 To 5:For j=0 To 5:Read maj_shifts(i,j):Next:Next
- For i=0 To 5:For j=0 To 5:Read min_shifts(i,j):Next:Next
-
- ;Read instrument names
- For i=0 To 4
- Read insts(i)
- Next
-
- ;Read the rest of the chromatic scale
- ;It is necessary to split it between two instruments
- ;two octaves apart
-
- For i=46 To 71
- chrom_scale(i)\period=chrom_scale(i-24)\period
- chrom_scale(i)\adr_sound=Addr Sound(1)
- chrom_scale(i)\ss=1
- chrom_scale2(i)\period=chrom_scale2(i-24)\period
- chrom_scale2(i)\adr_sound=Addr Sound(3)
- chrom_scale2(i)\ss=3
- Next
-
- ;Set out diatonic major and minor scales
- For i=0 To 11:wi=0
- For j=0 To 71
- ij=((i+j) MOD 12)
- If ij=0 OR ij=2 OR ij=4 OR ij=5 OR ij=7 OR ij=9 OR ij=11
- maj_scale(i,wi)=j:wi+1
- EndIf
- Next:Next
- For i=0 To 11:wi=0
- For j=0 To 71
- ij=((i+j) MOD 12)
- If ij=0 OR ij=2 OR ij=3 OR ij=5 OR ij=7 OR ij=8 OR ij=11
- min_scale(i,wi)=j:wi+1
- EndIf
- Next:Next
-
- ;Establish the positions of the tonic notes for each scale
- For i=0 To 11
- j=-1
- Repeat
- j+1
- Until maj_scale(i,j)+6=maj_scale(i,j+3) AND maj_scale(i,j)+2=maj_scale(i,j+1)
- maj_tonics(i)=j+4
- Next
- For i=0 To 11
- j=-1
- Repeat
- j+1
- Until min_scale(i,j)+3=min_scale(i,j+1)
- min_tonics(i)=j+2
- Next
-
- While Joyb(1)=0
-
- ;Clear instruments and load new ones
- Free Sound 0
- Free Sound 1
- Free Sound 2
- Free Sound 3
- Gosub getinst
-
- ;Generate tune
- ;To write a routine to compose even a simple meaningful
- ;melody is VERY DIFFICULT. This only scratches the surface
- ;of what promises to be a very interesting investigation.
- motifs(0)=Rnd(7)
-
- i=1:last_note.w=motifs(0)
- If Rnd(100)>50:tune_dirn.w=1:Else:tune_dirn=-1:EndIf
- density.w=50+Rnd(40):spaces.w=Rnd(4)+1
- If Rnd(100)>80:spaces.w=Rnd(4):Else:spaces=0:EndIf
- For i=1 To 39
- If Rnd(100)<density
- If Rnd(100)>70
- tune_dirn=-tune_dirn
- EndIf
- motifs(i)=(last_note+tune_dirn*(Rnd(spaces)+1)+8) MOD 8
- last_note=motifs(i)
- Else
- motifs(i)=999
- EndIf
- Next
-
- ;Clear fugue data to rests and set tempo
- main_temp.w=7+Rnd(10)
- For i=0 To fl1:For j=0 To 3:fugue(i,j)=999:Next:tempi(i)=main_temp:Next
- For i=0 To 15
- tempi(496+i)=tempi(0)+2*i
- Next i
-
- ;Set out modulations
- piece_mode.w=Int(Rnd(2))
- If Rnd(100)>50:piece_mode=0:Else:piece_mode=1:EndIf
- piece_key.w=100*piece_mode+Int(Rnd(12))
- modes(0)=0
- Repeat
- For i=32 To 480 Step 32
- If piece_mode.w=0
- modes(i)=maj_shifts(modes(i-32),Int(Rnd(maj_shifts(modes(i-32),1))+2))
- Else
- modes(i)=min_shifts(modes(i-32),Int(Rnd(min_shifts(modes(i-32),1))+2))
- EndIf
- Next
- Until modes(480)=0
- For i=0 To 480 Step 32
- If piece_mode=0
- r.w=maj_shifts(modes(i),0)
- If r>90:q.w=100 Else:q=0:EndIf
- r=(((r MOD 100)+piece_key) MOD 12)+q
- Else
- r.w=min_shifts(modes(i),0)
- If r>90:q.w=100 Else:q=0:EndIf
- r=(((r MOD 100)+piece_key-100) MOD 12)+q
- EndIf
- For j=0 To 31:modes(i+j)=r:Next
- Next
-
- ;Insert opening modulations
- If piece_mode=0
- sub_dom=(piece_key+5) MOD 12
- Else
- sub_dom=((piece_key-95) MOD 12)+100
- EndIf
- For i=0 To 31:modes(i)=piece_key:Next i
- For i=32 To 63:modes(i)=sub_dom:Next i
- For i=64 To 95:modes(i)=piece_key:Next i
- For i=96 To 127:modes(i)=sub_dom:Next i
-
- ;Write fixed sections
- ;Could easily be adapted to write sequences and stretto
-
- ;Opening
- For i=0 To 3
- If piece_mode=0
- Select i
- Case 0:spitch.w=maj_tonics(modes(i*32))+14
- Case 1:spitch=maj_tonics(modes(i*32))+7
- Case 2:spitch=maj_tonics(modes(i*32))+14
- Case 3:spitch=maj_tonics(modes(i*32))+7
- End Select
- Else
- Select i
- Case 0:spitch=min_tonics(modes(i*32)-100)+21
- Case 1:spitch=min_tonics(modes(i*32)-100)+14
- Case 2:spitch=min_tonics(modes(i*32)-100)+14
- Case 3:spitch=min_tonics(modes(i*32)-100)+7
- End Select
- EndIf
- For j=0 To fix_length-1
- ii.w=fix_length*i+j
-
- For k=i To 3
- fixed(ii,k)=1
- Next k
- If i<3
- For k=i To 3:fugue(ii,k)=999:Next k
- EndIf
- key=modes(ii):mm=motifs(j)
- If mm=999
- fugue(ii,i)=999
- Else
- If key>90
- key-100
- fugue(ii,i)=min_scale(key,spitch+mm)
- Else
- fugue(ii,i)=maj_scale(key,spitch+mm)
- EndIf
- EndIf
- Next:Next
-
- ;Write final cadence
- For i=504 To 511
- For j=0 To 3
- fugue(i,j)=999:fixed(i,j)=1
- Next:Next
- key=modes(504)
- If key>=100:key-100:EndIf
- fugue(504,0)=maj_scale(key,maj_tonics(key))
- fugue(504,1)=maj_scale(key,maj_tonics(key)+9)
- fugue(504,2)=maj_scale(key,maj_tonics(key)+11)
- fugue(504,3)=maj_scale(key,maj_tonics(key)+14)
-
- ;Main composition loop
- ;The algorithm is very simple.For each bar and voice a random section of tune
- ;is selected. An attempt is made to place it so that all resulting
- ;harmonies belong to the "legitimate" table. This is a lot easier
- ;than it seems because any chord TYPE (major, minor, augmented 9th or
- ;what have you as opposed to inversions or positions) is completely
- ;characterised by a partition of twelve. A chromatic scale is a cyclic
- ;group of order twelve.
-
- ;If the section of melody cannot be fitted to produce "legitimate"
- ;harmonies then it is transposed tonally in whatever key predominates
- ;(the modulations are set out in advance). If it does not fit after
- ;trying all seven scale transpositions it is discarded and a new one
- ;selected.
- Locate 2,10:NPrint "NOW COMPOSING BAR "
- For ii.w=0 To 508 Step 4:Locate 22,10:NPrint (ii/8)+1:For voice.w=0 To 3
- If Joyb(1)<>0:Pop For:Goto fin:EndIf
- If fixed(ii,voice)=1:Goto w1:EndIf
- Repeat
- If ii>=400 AND ii<500
- mstart.w=Int(Rnd(5))*4:spitch.w=15+Int(Rnd(15)):s=-1
- Else
- If Rnd*100>10
- mstart.w=lastmstart(voice):spitch.w=15+Int(Rnd(15)):s=-1
- Else
- mstart.w=Int(Rnd(10))*4:spitch.w=15+Int(Rnd(15)):s=-1
- EndIf
- EndIf
- Repeat
- s+1
- k=-1
- Repeat
- k+1
- key=modes(ii+k):mm=motifs(mstart+k)
- If mm=999
- fugue(ii+k,voice)=999
- Else
- If key>90
- key-100
- fugue(ii+k,voice)=min_scale(key,spitch+mm-s)
- Else
- fugue(ii+k,voice)=maj_scale(key,spitch+mm-s)
- EndIf
- EndIf
- fi=ii+k:Gosub chord_type
- flag=0:j=-1
- Repeat
- j+1
- If ctype=legit_chord(j):flag=1:EndIf
- Until j=24 OR flag=1
- Until k=3 OR flag=0
- Until s=6 OR flag=1
- Until flag=1
- lastmstart(voice)=mstart
- w1:Next:Next
-
- ;Play fugue
-
- Locate 2,12:Print "NOW PLAYING BAR "
- For i=0 To fl1
- fi=i:Gosub chord_type:fugue_ctype(i)=ctype
- Next i
- For i=0 To fl-8
- If Joyb(1)<>0:Pop For:Goto fin:EndIf
- Locate 22,12:Print Int(i/8+1)
- If fugue(i,0)<999
- Poke.w chrom_scale(fugue(i,0))\adr_sound+4,chrom_scale(fugue(i,0))\period
- Sound chrom_scale(fugue(i,0))\ss,1
- EndIf
- If fugue(i,1)<999
- Poke.w chrom_scale2(fugue(i,1))\adr_sound+4,chrom_scale2(fugue(i,1))\period
- Sound chrom_scale2(fugue(i,1))\ss,2
- EndIf
- If fugue(i,2)<999
- Poke.w chrom_scale(fugue(i,2))\adr_sound+4,chrom_scale(fugue(i,2))\period
- Sound chrom_scale(fugue(i,2))\ss,4
- EndIf
- If fugue(i,3)<999
- Poke.w chrom_scale2(fugue(i,3))\adr_sound+4,chrom_scale2(fugue(i,3))\period
- Sound chrom_scale2(fugue(i,3))\ss,8
- EndIf
- VWait tempi(i)
- Next
-
- VWait 200
- Wend
- fin:VWait 50
- End
-
- ;Subroutine to analyse harmonies
- ;This is based on an article I wrote in the N.Z Mathematics
- ;Magazine many years ago about the possibility of using a
- ;simple theorem on groups to characterise chord types.
- chord_type:
- For q=0 To 3:t(q)=fugue(fi,q):Next q
- Sort t()
- If t(0)=999:ctype=12:Return:EndIf
- For m=1 To 3
- If t(m)=999:t(m)=t(m-1):EndIf
- Next
- For q=0 To 3:t(q)=t(q) MOD 12:Next q
- Sort t()
- For ri=1 To 3
- interval(ri-1)=t(ri)-t(ri-1)
- Next
- interval(3)=12-interval(0)-interval(1)-interval(2)
- wi=0
- For m=0 To 3
- interval2(m)=0
- If interval(m)>0:interval2(wi)=interval(m):wi+1:EndIf
- Next
- For m=0 To 3:interval(m)=interval2(m):Next
- ctype=interval(0)+interval(1)*11+interval(2)*121+interval(3)*1331
- Return
-
- getinst:
- inst1.w=Rnd(5):inst2.w=Rnd(5)
- LoadSound 0,insts(inst1)+"_low"
- LoadSound 1,insts(inst1)+"_high"
- LoadSound 2,insts(inst2)+"_low"
- LoadSound 3,insts(inst2)+"_high"
- i7:VWait 100:Return
-